<template>
  <div :class="`${boxCls} ${boxTransitionCls}`" :style="boxStyle">
    <div :class="shimCls" :style="shimStyle">
      <div
        v-for="item in faceConfigList_res"
        :key="item.position"
        :class="`${itemCls} ${getFaceClass(item)}`"
        :style="getFaceStyle(item)"
        @mousedown="startTurn(item.position)"
        @mouseup="endTurn(item.position)"
      >
        <!-- <div v-text="FacePositionBase[item.position]"></div> -->
        <!-- <div v-text="coord"></div> -->
        <!-- <div v-text="currentCoord"></div> -->
      </div>
    </div>
  </div>
</template>
<script lang="ts">
import {
  Vue,
  Component,
  Prop,
  InjectReactive,
  Watch,
  Emit,
} from "vue-property-decorator";
import {
  Size,
  Axis,
  FaceColor,
  FacePositionBase,
  FaceColorConfig,
  RotateRatioConfig,
  TranslateRatioConfig,
  getCubeConfigBase,
  FaceConfigItem,
  CubeCoord,
  getPxVal,
  getCubeFaceTransformStyle,
  FaceConfigObjectInPosition,
  getPositionChangeOrder,
  OrderPosition,
} from "@/common/utils";
import { parseInt } from "lodash";

const prefix = "base-cube";
@Component({
  name: "BaseCube",
  inheritAttrs: false,
})
export default class extends Vue {
  // props
  @Prop({
    type: Array,
  })
  readonly coord!: CubeCoord;

  @Prop({
    type: Array,
  })
  readonly currentCoord!: CubeCoord;

  @Prop({
    type: Object,
    default: () => ({}),
  })
  readonly faceColorConfig!: FaceColorConfig;

  @Prop({
    type: Object,
    default: () => ({}),
  })
  readonly rotateRatioConfig!: RotateRatioConfig;

  @Prop({
    type: Object,
    default: () => ({}),
  })
  readonly translateRatioConfig!: TranslateRatioConfig;

  @Prop({
    type: Object,
    default: () => ({}),
  })
  readonly shimRotateRatioConfig!: RotateRatioConfig;

  @Prop({
    type: Boolean,
    default: true,
  })
  readonly boxTransition!: boolean;

  // inject
  @InjectReactive()
  readonly size!: Size;

  @InjectReactive()
  readonly spaceRatio!: number;

  // data
  faceConfigList: FaceConfigItem[] = [];
  FacePositionBase = FacePositionBase;

  // computed
  get boxCls(): string {
    return prefix;
  }
  get boxTransitionCls(): string {
    return this.boxTransition ? `${this.boxCls}_transition` : "";
  }
  get itemCls(): string {
    return `${this.boxCls}-item`;
  }
  get shimCls(): string {
    return `${this.boxCls}-shim`;
  }
  get xRotateRatio(): number {
    return this.rotateRatioConfig[Axis.x];
  }
  get yRotateRatio(): number {
    return this.rotateRatioConfig[Axis.y];
  }
  get zRotateRatio(): number {
    return this.rotateRatioConfig[Axis.z];
  }
  get faceConfigList_res(): FaceConfigItem[] {
    return this.faceConfigList.filter(
      (item) => item.color !== FaceColor.default
    );
    // return this.faceConfigList;
  }
  get boxStyle(): {
    transform: string;
  } {
    const {
      size,
      getRotateTransform,
      rotateRatioConfig,
      translateRatioConfig,
      spaceRatio,
    } = this;
    const xTranslateRatio = translateRatioConfig[Axis.x];
    const yTranslateRatio = translateRatioConfig[Axis.y];
    const zTranslateRatio = translateRatioConfig[Axis.z];
    const translateSize = size * spaceRatio;
    return {
      transform: `${getRotateTransform(rotateRatioConfig)} translate3d(${
        xTranslateRatio * translateSize
      }px, ${yTranslateRatio * translateSize}px, ${
        zTranslateRatio * translateSize
      }px)`,
    };
  }
  get shimStyle(): {
    transform: string;
  } {
    const { shimRotateRatioConfig, getRotateTransform } = this;
    return {
      transform: getRotateTransform(shimRotateRatioConfig),
    };
  }
  get faceConfigObjectInPosition(): FaceConfigObjectInPosition {
    return this.faceConfigList.reduce((res, item) => {
      res[item.position] = item;
      return res;
    }, {} as FaceConfigObjectInPosition);
  }

  // methods
  @Emit("startTurn")
  startTurn<T>(data: T): T {
    return data;
  }
  @Emit("endTurn")
  endTurn<T>(data: T): T {
    return data;
  }
  @Emit("afterUpdated")
  afterUpdated(): void {
    return;
  }
  getRotateTransform(config: RotateRatioConfig): string {
    const x = config[Axis.x];
    const y = config[Axis.y];
    const z = config[Axis.z];
    return `rotateX(${x * 90}deg) rotateY(${y * 90}deg) rotateZ(${z * 90}deg) `;
  }
  getFaceStyle({ position, color: backgroundColor }: FaceConfigItem): {
    backgroundColor: string;
    transform: string;
    boxShadow: string;
  } {
    const { size } = this;
    const pxVal = getPxVal(size);
    const res = {
      width: pxVal,
      height: pxVal,
      backgroundColor,
      transform: getCubeFaceTransformStyle(position, size, 90),
      boxShadow: `0 0 2px #000, ${
        backgroundColor !== FaceColor.default
          ? "0 0 2px #000 inset"
          : "0 0 2px #fff inset"
      }`,
    };
    return res;
  }
  getFaceClass({ position }: FaceConfigItem): string {
    return `${this.itemCls}_${position}`;
  }
  updateFaceConfigList(orderPosition: OrderPosition): void {
    const { faceConfigList } = this;
    const l = orderPosition.length;

    const orderPositionMap = orderPosition.reduce(
      (res, item, index) => {
        const nextIndex = (index + 1) % l;
        const nextPosition = orderPosition[nextIndex];
        const nextConfig = faceConfigList.find(
          (item) => item.position === nextPosition
        );
        res[item] = nextConfig.color;
        return res;
      },
      {} as {
        [key in FacePositionBase]: FaceColor;
      }
    );

    faceConfigList.forEach((item) => {
      if (orderPosition.includes(item.position)) {
        item.color = FaceColor.default;
      }
    });
    orderPosition
      .map((position) =>
        faceConfigList.find((item) => item.position === position)
      )
      .forEach((item) => {
        item.color = orderPositionMap[item.position];
      });
  }

  // 生命周期

  // watch
  @Watch("faceColorConfig", { immediate: true })
  onFaceColorListChange(): void {
    const { faceColorConfig } = this;

    const cubeConfigBaseKeys = Object.keys(getCubeConfigBase()).map((item) =>
      parseInt(item)
    ) as FacePositionBase[];

    this.faceConfigList = cubeConfigBaseKeys.map((position) => {
      const defineColor = faceColorConfig[position];
      const color = defineColor || FaceColor.default;
      return {
        position,
        color,
        text: `${position}-${color}`,
      };
    });
  }

  @Watch("boxTransition")
  onBoxTransitionChange(): void {
    if (this.boxTransition === false) {
      this.$nextTick(() => {
        this.afterUpdated();
      });
    }
  }

  @Watch("xRotateRatio")
  onXRotateRatioChange(n: number, o: number): void {
    // console.log(269, this.boxTransition);
    // console.log(317, "xRotateRatio变化");
    !this.boxTransition &&
      this.updateFaceConfigList(
        getPositionChangeOrder(this.faceConfigObjectInPosition, Axis.x, n < o)
      );
  }
  @Watch("yRotateRatio")
  onyRotateRatioChange(n: number, o: number): void {
    // console.log(322, "yRotateRatio变化");
    !this.boxTransition &&
      this.updateFaceConfigList(
        getPositionChangeOrder(this.faceConfigObjectInPosition, Axis.y, n < o)
      );
  }
  @Watch("zRotateRatio")
  onZRotateRatioChange(n: number, o: number): void {
    // console.log(327, "zRotateRatio变化");
    !this.boxTransition &&
      this.updateFaceConfigList(
        getPositionChangeOrder(this.faceConfigObjectInPosition, Axis.z, n < o)
      );
  }
}
</script>

<style lang="less" scoped>
@prefix: base-cube;
.@{prefix}{
  @t:300ms;
  position: absolute;
  transform-style: preserve-3d;
  left: 50%;
  top: 50%;
  color: #000;
  &_transition{
    transition: transform @t;
  }
  &-shim{
    position: relative;
    transform-style: preserve-3d;
  }
  &-item{
    transition: height @t, width @t;
    // opacity: 0.8;
    position: absolute;
    width: 100%;
    height: 100%;
    border-radius: 1px;
    display: flex;
    flex-direction: column;
    justify-content: space-around;
    align-items: center;
  }
}
</style>
